/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with
    Amrita ITEWS if not then it can be viewed here:
    http://itews.amrita.ac.in/license.html.


    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/


    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews


    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa
    Vidyapeetham as part of the project titled,"Intelligent & Interactive
    Telematics using Emerging Wireless Technologies for Transport Systems
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment
    Council (TIFAC), India.
***/

/*! \file str_scan.c
    \brief String Scan Library Source File.
*/

#include <str_scan.h>

static int skip_atoi( const char **s );



/*! \brief str_scan_strtoul - convert a string to an unsigned long.
    \param cp: The start of the string.
    \param endp: A pointer to the end of the parsed string will be placed here.
    \param base: The number base to use.
    \return unsigned long value.
 */
unsigned long str_scan_strtoul(const char *cp,char **endp,unsigned int base)
{
    unsigned long result = 0,value;

    if (!base) {
        base = 10;
        if (*cp == '0') {
            base = 8;
            cp++;
            if ((*cp == 'x') && isxdigit(cp[1])) {
                cp++;
                base = 16;
            }
        }
    }
    while (isxdigit(*cp) &&
           (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
        result = result*base + value;
        cp++;
    }
    if (endp)
        *endp = (char *)cp;
    return result;
}

/*! \brief str_scan_strtol - convert a string to a signed long.
    \param cp: The start of the string.
    \param endp: A pointer to the end of the parsed string will be placed here.
    \param base: The number base to use.
    \return signed long value.
 */
long str_scan_strtol(const char *cp,char **endp,unsigned int base)
{
    if(*cp=='-')
        return -str_scan_strtoul(cp+1,endp,base);
    return str_scan_strtoul(cp,endp,base);
}

/*! \brief str_scan_strtoull - convert a string to an unsigned long long.
    \param cp: The start of the string.
    \param endp: A pointer to the end of the parsed string will be placed here.
    \param base: The number base to use.
    \return unsigned long long value.
 */
unsigned long long str_scan_strtoull(const char *cp,char **endp,unsigned int base)
{
    unsigned long long result = 0,value;

    if (!base) {
        base = 10;
        if (*cp == '0') {
            base = 8;
            cp++;
            if ((*cp == 'x') && isxdigit(cp[1])) {
                cp++;
                base = 16;
            }
        }
    }
    while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
        ? toupper(*cp) : *cp)-'A'+10) < base) {
        result = result*base + value;
        cp++;
    }
    if (endp)
        *endp = (char *)cp;
    return result;
}

/*! \brief str_scan_strtoll - convert a string to a signed long long.
    \param cp: The start of the string.
    \param endp: A pointer to the end of the parsed string will be placed here.
    \param base: The number base to use.
    \return long long value.
 */
long long str_scan_strtoll(const char *cp,char **endp,unsigned int base)
{
    if(*cp=='-')
        return -str_scan_strtoull(cp+1,endp,base);
    return str_scan_strtoull(cp,endp,base);
}

/*! \brief str_scan_vscanf - Unformat a buffer into a list of arguments.
    \param buf: input buffer.
    \param fmt: format of buffer.
    \param args: arguments.
    \return numbytes successfully.
 */
int str_scan_vscanf(const char * buf, const char * fmt, va_list args)
{
    const char *str = buf;
    char *next;
    int num = 0;
    int qualifier;
    int base;
    int field_width = -1;
    int is_sign = 0;

    while(*fmt && *str) {
        /* skip any white space in format */
        /* white space in format matchs any amount of
         * white space, including none, in the input.
         */
        if (isspace(*fmt)) {
            while (isspace(*fmt))
                ++fmt;
            while (isspace(*str))
                ++str;
        }

        /* anything that is not a conversion must match exactly */
        if (*fmt != '%' && *fmt) {
            if (*fmt++ != *str++)
                break;
            continue;
        }

        if (!*fmt)
            break;
        ++fmt;

        /* skip this conversion.
         * advance both strings to next white space
         */
        if (*fmt == '*') {
            while (!isspace(*fmt) && *fmt)
                fmt++;
            while (!isspace(*str) && *str)
                str++;
            continue;
        }

        /* get field width */
        if (isdigit(*fmt))
            field_width = skip_atoi(&fmt);

        /* get conversion qualifier */
        qualifier = -1;
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z') {
            qualifier = *fmt;
            fmt++;
        }
        base = 10;
        is_sign = 0;

        if (!*fmt || !*str)
            break;

        switch(*fmt++) {
        case 'c':
        {
            char *s = (char *) va_arg(args,char*);
            if (field_width == -1)
                field_width = 1;
            do {
                *s++ = *str++;
            } while(field_width-- > 0 && *str);
            num++;
        }
        continue;
        case 's':
        {
            char *s = (char *) va_arg(args, char *);
            if(field_width == -1)
                field_width = INT_MAX;
            /* first, skip leading white space in buffer */
            while (isspace(*str))
                str++;

            /* now copy until next white space */
            while (*str && !isspace(*str) && field_width--) {
                *s++ = *str++;
            }
            *s = '\0';
            num++;
        }
        continue;
        case 'n':
            /* return number of characters read so far */
        {
            int *i = (int *)va_arg(args,int*);
            *i = str - buf;
        }
        continue;
        case 'o':
            base = 8;
            break;
        case 'x':
        case 'X':
            base = 16;
            break;
        case 'd':
        case 'i':
            is_sign = 1;
        case 'u':
            break;
        case '%':
            /* looking for '%' in str */
            if (*str++ != '%')
                return num;
            continue;
        default:
            /* invalid format; stop here */
            return num;
        }

        /* have some sort of integer conversion.
         * first, skip white space in buffer.
         */
        while (isspace(*str))
            str++;

        if (!*str || !isdigit(*str))
            break;

        switch(qualifier) {
        case 'h':
            if (is_sign) {
                short *s = (short *) va_arg(args,short *);
                *s = (short) str_scan_strtol(str,&next,base);
            } else {
                unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
                *s = (unsigned short) str_scan_strtoul(str, &next, base);
            }
            break;
        case 'l':
            if (is_sign) {
                long *l = (long *) va_arg(args,long *);
                *l = str_scan_strtol(str,&next,base);
            } else {
                unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
                *l = str_scan_strtoul(str,&next,base);
            }
            break;
        case 'L':
            if (is_sign) {
                long long *l = (long long*) va_arg(args,long long *);
                *l = str_scan_strtoll(str,&next,base);
            } else {
                unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
                *l = str_scan_strtoull(str,&next,base);
            }
            break;
        case 'Z':
        {
            size_t *s = (size_t*) va_arg(args,size_t*);
            *s = (size_t) str_scan_strtoul(str,&next,base);
        }
        break;
        default:
            if (is_sign) {
                int *i = (int *) va_arg(args, int*);
                *i = (int) str_scan_strtol(str,&next,base);
            } else {
                unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
                *i = (unsigned int) str_scan_strtoul(str,&next,base);
            }
            break;
        }
        num++;

        if (!next)
            break;
        str = next;
    }
    return num;
}

/*! \brief str_scan_scanf - Unformat a buffer into a list of arguments.
    \param buf: input buffer.
    \param fmt: formatting of buffer.
    \param ...: resulting arguments.
    \return data Length if success.
 */
int str_scan_scanf(const char * buf, const char * fmt, ...)
{
    va_list args;
    int i;

    va_start(args,fmt);
    i = str_scan_vscanf(buf,fmt,args);
    va_end(args);
    return i;
}

static int skip_atoi(const char **s)
{
    int i=0;

    while (isdigit(**s))
        i = i*10 + *((*s)++) - '0';
    return i;
}
